跳到主要内容

Go 编写的简单代理

package main

import (
"bufio"
"encoding/base64"
"flag"
"io"
"io/ioutil"
"log"
"net"
"net/http"
"strings"
"time"
)

func transfer(destination io.WriteCloser, source io.ReadCloser) {
defer destination.Close()
defer source.Close()
io.Copy(destination, source)
}

func main() {
host := flag.String("host", ":80", "host proxy server")
auth := flag.String("auth", "", "authentication to for client to connect proxy, ex: username:password")

flag.Parse()

listener, err := net.Listen("tcp", *host)
if err != nil {
log.Fatal(err)
}
defer listener.Close()

for {
conn, err := listener.Accept()
if err != nil {
log.Fatal(err)
}

go func(c net.Conn) {
br := bufio.NewReader(c)
req, err := http.ReadRequest(br)
if err != nil {
log.Println("buffer: ", err)
return
}

if req.Method == http.MethodConnect {
if *auth != "" {
clientAuth := req.Header.Get("Proxy-Authorization")
if clientAuth == "" {
response := &http.Response{
StatusCode: http.StatusProxyAuthRequired,
ProtoMajor: 1,
ProtoMinor: 1,
}
response.Write(c)
c.Close()
return
}

serverAuth := "Basic " + base64.StdEncoding.EncodeToString([]byte(*auth))
if clientAuth != serverAuth {
response := &http.Response{
StatusCode: http.StatusUnauthorized,
ProtoMajor: 1,
ProtoMinor: 1,
}
response.Write(c)
c.Close()
return
}
}

response := &http.Response{
StatusCode: 200,
ProtoMajor: 1,
ProtoMinor: 1,
}
response.Write(c)

destConn, err := net.DialTimeout("tcp", req.URL.Host, 10*time.Second)
if err != nil {
response := &http.Response{
StatusCode: http.StatusRequestTimeout,
ProtoMajor: 1,
ProtoMinor: 1,
}
response.Write(c)
return
}

go transfer(destConn, c)
go transfer(c, destConn)

} else {
response := &http.Response{
StatusCode: http.StatusRequestTimeout,
ProtoMajor: 1,
ProtoMinor: 1,
Body: ioutil.NopCloser(strings.NewReader("hello world")),
}
response.Write(c)
c.Close()
return
}
}(conn)
}
}